/*****************************************************************************************
 * 文件名   ：EEROM.c
 * 描述     ：MCU型号：中颖 SH79F083A   
 * 编译器   ：KEIL_C51_V5.14.2
 * 版本     ：V1.0.0
 * 作者     ：夏毅承
 * 修改时间 ：2016-10-14
 *
 * 说明     ：该文件主要完成对类EEROM的读写操作
 *****************************************************************************************/

#include "..\Inc\Includes.h" 
#include "intrins.h"
/******************************************************************************************
类EEROM一共2*256B  至少10万次的有效次数	
扇区地址范围
0x0000-----0x00ff  
0x0100-----0x01ff  

******************************************************************************************/
//扇区擦除
void SSP_Erase(unsigned int Addr)
{
  CY = EA;
	EA = 0;
	FLASHCON = 0x01;   																																			//访问类EEROM
	XPAGE = ((Addr>>8)&0x07); 																															//页
	IB_CON1 = 0xE6;
	IB_CON2 = 0x05;
	IB_CON3 = 0x0A;
	IB_CON4 = 0x09;
	IB_CON5 = 0x06;
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	XPAGE = 0x00;
	FLASHCON = 0x00;
	EA = CY;																																								//恢复中断
}

//单字节的写操作
void SSP_Write(unsigned int Addr, unsigned char Data)
{
  CY = EA;																																								//保存中断状态
	EA = 0;																																									//step 1 ,关闭中断
	FLASHCON = 0x01;      																																	//访问类EEROM
	XPAGE = ((Addr>>8)&0x07);																																//step 2 ,设置擦除的扇区,256一个扇区，取扇区3bit	 //页
	IB_DATA = Data;	 																																				//step 3,待编程数据
	IB_OFFSET = Addr& 0x00FF;																																// 被操作扇区的 8bit  //偏移地址
	IB_CON1 = 0x6E;																																					//step 4,设置写操作
	IB_CON2 = 0x05;																																					//时序的要求，固定不变
	IB_CON3 = 0x0A;																																					//时序的要求，固定不变
	IB_CON4 = 0x09;																																					//时序的要求，固定不变
	IB_CON5 = 0x06;																																					//时序的要求，固定不变
	_nop_();																																								//step 5,进入写状态
	_nop_();
	_nop_();
	_nop_();
	XPAGE = 0x00;																																						//step 6,清零页寄存器
	FLASHCON = 0x00;																																				//退出类EEPROM
	EA = CY;	  																																						//恢复中断
}

//多字节的写操作
void SSP_WriteMultiByte(unsigned int Addr, unsigned char *p)
{
	unsigned char				u8_loop;
	
	SSP_Erase(Addr);																																				//写之前要先擦除
	
	SSP_Write(ADDR_VOLTAGE_OFFSET,U8_VoltageOffset);																				//修改电压调整偏移量
	SSP_Write(ADDR_BLACKLIGHT_CLASS,U8_BlacklightClass);																		//保存修改背光亮度
	SSP_Write(ADDR_MILEAGE_UNIT,KMH_OR_MPH_Flag);																						//保存里程单位
	//SSP_Write(ADDR_BATTERY_VOL_CLASS,U8_RatedVoltageValue);																	//保存额定电池电压设置
	SSP_Write(ADDR_SLEEP_TIME,U8_SleepTimeValue);																						//保存休眠时间


	for (u8_loop=0;u8_loop<SEND_LENGTH_OF_ARRAY;u8_loop++)																	//保存数据
	{
		SSP_Write((Addr+u8_loop), *(p+u8_loop));																							//
	}
}


//字节读取
unsigned char SSP_Read(unsigned int Addr)
{ 
  unsigned char data_temp = 0;
	
  FLASHCON = 1;  																																					//访问类EEROM
  data_temp = *(unsigned char code *)(Addr);
  FLASHCON = 0;
  return (data_temp);
}

//多字节读取发送参数数据
void SSP_ReadUartSendData(unsigned int Addr)
{
	unsigned char			u8_loop;
	
	U8_VoltageOffset = SSP_Read(ADDR_VOLTAGE_OFFSET);																				//读取电压调整偏移量
	U8_BlacklightClass = SSP_Read(ADDR_BLACKLIGHT_CLASS);																		//读取背光亮度
	KMH_OR_MPH_Flag = SSP_Read(ADDR_MILEAGE_UNIT);																					//读取里程单位
	//U8_RatedVoltageValue = SSP_Read(ADDR_BATTERY_VOL_CLASS);																//读取里额定电池电压设置
	U8_SleepTimeValue = SSP_Read(ADDR_SLEEP_TIME);																					//读取休眠时间
	
	for (u8_loop=0;u8_loop<16;u8_loop++)																										//发送总共20个字节前三和后一个不需要保存
	{
		U8_UartSendData[SAVE_ADDR_OFFSET+u8_loop] = SSP_Read((Addr+u8_loop+SAVE_ADDR_OFFSET));
	}
}

 /**************************************************************************
 * 函数名		：OdographNumberRead()
 * 输入参数 ：无
 * 输出参数 ：无
 * 返回值  	：无
 * 修改时间 ：2017-09-15
 * 函数描述 ：读存储的EEprOM中的里程数,上电初始化调用
 *						存储数据结构：						
 *						┌────────┬─────────────┬─────────────┬────────┐
 *						│	前导值 │有效值高字节 │有效值低字节 │ 后导值	│
 *						├────────┼─────────────┼─────────────┼────────┤
 *						│ 0xAA	 │	 HighByte	 │	 LowByte   │ 0x55		│
 *						└────────┴─────────────┴─────────────┴────────┘
 *						其中前导和后导值为固定的0XAA 和0x55,里程有效值才是有效的，如果前导和后导值
 *						不是固定值则里程数据为无效值。
 *						读取方法：
 *						通过里程值在EEPROM中的基准值开始读，一直读到前导和后导值不是固定值说明前一次
 *						的值是有效里程数据，然后将前一次的里程值放里程寄存器，将存储偏移量放写EEPROM
 *						地址偏移量中，完成读里程操作。
 *************************************************************************/	
void OdographNumberRead(void)
{
	unsigned char								u8_read_count = 0;
	unsigned char								u8_leading_value = 0,																				//有效数据的前引导值，
															u8_trailing_value = 0;																			//有效数据的后引导值
	unsigned int								u16_odograph_addr = 0,
															u16_odograph_number_temp = 0;
	
	u16_odograph_addr = ADDR_EXCURSION_ODO;
	U16_OdographNumber = 0x00;																															//上电读里程数先清零

//模拟EEPROM一页是256个字节，每组数据占4个字节，一页只记录62个有效数据	，所以有64*4=248
//个字节，就说明这一页满了
	do
	{
		u8_leading_value = SSP_Read(u16_odograph_addr);																				//读取前导值
		u16_odograph_addr++;
		u16_odograph_number_temp = (SSP_Read(u16_odograph_addr)<<8);													//读取高位有效值值
		u16_odograph_addr++;
		u16_odograph_number_temp += SSP_Read(u16_odograph_addr);															//读取低位有效值值
		u16_odograph_addr++;
		u8_trailing_value = SSP_Read(u16_odograph_addr);																			//读取后导值
		u16_odograph_addr++;
		
		if ((u8_leading_value==0xAA)&&(u8_trailing_value==0x55))															//前导和后导值是正确的保存本次读取的里程值
		{
			U16_OdographNumber = u16_odograph_number_temp;																			//
		}
	
	}
	while (((u8_leading_value==0xAA)&&(u8_trailing_value==0x55))&&(u16_odograph_addr<SAVE_ADDR_OFFSET_END));		//前导或后导数据不是固定值，或者读取了62个数据了，完成读取
	
	//SSP_Erase(ADDR_EXCURSION_ODO);																													//读完里程就将扇区擦掉，重新记录里程值
	U8_OdographSaveAddressOffset = 0x00;																										//偏移量清零
}

/**************************************************************************
 * 函数名		：OdographNumberWrite()
 * 输入参数 ：无
 * 输出参数 ：无
 * 返回值  	：无
 * 修改时间 ：2017-09-15
 * 函数描述 ：读存储的EEprOM中的里程数,里程数每加一公里调用
 *						存储数据结构：						
 *						┌────────┬─────────────┬─────────────┬────────┐
 *						│	前导值 │有效值高字节 │有效值低字节 │ 后导值	│
 *						├────────┼─────────────┼─────────────┼────────┤
 *						│ 0xAA	 │	 HighByte	 │	 LowByte   │ 0x55		│
 *						└────────┴─────────────┴─────────────┴────────┘
 *						其中前导和后导值为固定的0XAA 和0x55,里程有效值才是有效的，如果前导和后导值
 *						不是固定值则里程数据为无效值。
 *						保存方法：
 *						由输入参数给定的地址开始写数据，先写固定前导值，地址加1写里程值高字节，地址
 *						再加1，写里程低字节，地址再加1写固定后导值，再次地址值加1，然后将此时的地址
 *						偏移量加了偏移量寄存器，作为下一次要保存写入的地址，如果地址偏移量到达了模拟
 *						EEPROM的末尾就将偏移地址复位。如果写数据之前地址偏移量是0就说明保存的页写满
 *						了，要擦除后才能使用。
 *************************************************************************/	
void OdographNumberWrite(unsigned int Addr)
{
	if (U8_OdographSaveAddressOffset==0x00)																									//偏移量为0说明一页被写完了，要擦除后重头重新保存数据
	{
		SSP_Erase(ADDR_EXCURSION_ODO);																												//将扇区擦掉，重新记录里程值
	}

	SSP_Write((Addr+U8_OdographSaveAddressOffset),0xAA);																		//写前导值
	U8_OdographSaveAddressOffset++;
	SSP_Write((Addr+U8_OdographSaveAddressOffset),(U16_OdographNumber>>8));									//写里程值高字节
	U8_OdographSaveAddressOffset++;
	SSP_Write((Addr+U8_OdographSaveAddressOffset),(U16_OdographNumber&0xFF));								//写里程值低字节
	U8_OdographSaveAddressOffset++;
	SSP_Write((Addr+U8_OdographSaveAddressOffset),0x55);																		//写后导值
	U8_OdographSaveAddressOffset++;

	if (U8_OdographSaveAddressOffset>=SAVE_ADDR_OFFSET_END)																	//如果到了一页的保存次数的末尾就将地址偏移量复位
	{
		U8_OdographSaveAddressOffset = 0x00;
	}
}
/**********************SINOWEALTH_SH79F083A**********END OF FILE*************************/
